﻿/***************************************************************************
    Copyright (c) Microsoft Corporation. All rights reserved.
    This code is licensed under the Visual Studio 
    Visualization and Modeling SDK license terms.

    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
***************************************************************************/

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data.EntityClient;
using System.Data.Objects;
using System.Diagnostics;
using System.IO;
using System.Linq;

namespace Microsoft.VisualStudio.ArchitectureTools.CodeRepositoryQuery
{
    /// <summary>
    /// Library routines for working with the code repository.
    /// </summary>
    public static class Utilities
    {
        /// <summary>
        /// Root part of the name for EDMX embedded resources for the query code model.
        /// </summary>
        private const string QueryModelMetadataRootName = "CodeRepository";


        #region Public methods

        /// <summary>
        /// Use the connection string information from the specified app config
        /// entry to create and return a new database context.
        /// </summary>
        /// <param name="configSettingName">The name of the configuration entry.</param>
        /// <returns>A new database connection.</returns>
        /// <exception cref="ConfigurationErrorsException">Thrown if the specified config entry could not be found.</exception>
        public static RepositoryEntities CreateNewDBContextFromConfigName(string configSettingName)
        {
            if (string.IsNullOrWhiteSpace(configSettingName))
            {
                throw new ArgumentNullException("configSettingName");
            }

            ConnectionStringSettings connSettings = GetSettingsFromConfig(configSettingName);

            return CreateNewDBContext(connSettings.ConnectionString, connSettings.ProviderName);
        }

        /// <summary>
        /// Creates and returns a new database context object.
        /// </summary>
        /// <param name="sqlConnectionString">The SQL connection to use. Cannot be null or empty.</param>
        /// <param name="providerName">The SQL provider. Cannot be null or empty.</param>
        /// <returns>The new database context.</returns>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification="The caller needs to dispose it: this is a cretion method")]
        public static RepositoryEntities CreateNewDBContext(string sqlConnectionString, string providerName = "System.Data.SqlClient")
        {
            if (string.IsNullOrWhiteSpace(sqlConnectionString))
            {
                throw new ArgumentNullException("sqlConnectionString");
            }
            if (string.IsNullOrWhiteSpace(providerName))
            {
                throw new ArgumentNullException("providerName");
            }

            EntityConnection connection = GetEntityConnection(sqlConnectionString, providerName);
            Debug.Assert(connection != null, "Entity connection should not be null");

            RepositoryEntities dbContext = new RepositoryEntities(connection);
            
            return dbContext;
        }

        #endregion

        #region Private methods
        /// <summary>
        /// Event handler to resolve an assembly (tries in current simulation application path)
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        /// <returns></returns>
        private static System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
        {
            System.Reflection.Assembly assembly = typeof(Utilities).Assembly;

            if (args.Name == assembly.FullName)
            {
                return assembly;
            }
            else
            {
                return null;
            }

        }
        /// <summary>
        /// Fetchs connection settings from the config file.
        /// </summary>
        /// <exception cref="ConfigurationErrorsException">Thrown if the specified configuration section does not exist.</exception>
        private static ConnectionStringSettings GetSettingsFromConfig(string configSettingName)
        {
            ConnectionStringSettings connSettings = ConfigurationManager.ConnectionStrings[configSettingName];
            if (connSettings == null)
            {
                string msg = string.Format(Properties.Resources.Culture, Properties.Resources.ErrorMissingConfigConnectionSetting, configSettingName);
                throw new ConfigurationErrorsException(msg);
            }
            return connSettings;
        }

        /// <summary>
        /// Creates and returns a new connection string.
        /// </summary>
        /// <param name="sqlConnectionString">The SQL connection to use. Cannot be null or empty.</param>
        /// <param name="providerName">The SQL provider. Cannot be null or empty.</param>
        /// <returns>A new entity connection instance.</returns>
        private static EntityConnection GetEntityConnection(string sqlConnectionString, string providerName)
        {
            if (string.IsNullOrWhiteSpace(sqlConnectionString))
            {
                throw new ArgumentNullException("sqlConnectionString");
            }
            if (string.IsNullOrWhiteSpace(providerName))
            {
                throw new ArgumentNullException("providerName");
            }
            return GetEntityConnectionString(sqlConnectionString, providerName, QueryModelMetadataRootName);
        }

        /// <summary>
        /// Builds and returns a new entity connection string.
        /// </summary>
        private static EntityConnection GetEntityConnectionString(string sqlConnectionString, string providerName, string resourceRootName)
        {
            EntityConnectionStringBuilder entityConnBuilder = new EntityConnectionStringBuilder();
            entityConnBuilder.Provider = providerName;
            entityConnBuilder.ProviderConnectionString = sqlConnectionString;
            string md = string.Format(System.Globalization.CultureInfo.InvariantCulture,
                "res://*/{0}.csdl|res://*/{0}.ssdl|res://*/{0}.msl",
                resourceRootName);
            entityConnBuilder.Metadata = md;

            return new EntityConnection(entityConnBuilder.ToString());
        }

        #endregion
    }
}
